%% @doc EMQX RabbitMQ 插件应用模块。
-module(emqx_plugin_rabbitmq_app).

-behaviour(application).
-emqx_plugin(?MODULE).

-include_lib("emqx/include/logger.hrl").
-include("emqx_plugin_rabbitmq.hrl"). %% 将包含插件特定的宏定义
-include_lib("kernel/include/file.hrl").

-export([start/2, stop/1]).

%% @doc 启动应用程序。
start(_StartType, _StartArgs) ->
    ?SLOG(info, #{msg => "starting_rabbitmq_plugin"}),
    case ensure_amqp_client_started() of
        ok ->
            copy_config_to_etc(),
            case emqx_plugin_rabbitmq_sup:start_link() of
                {ok, Sup} ->
                    handle_supervisor_started(Sup);
                {error, Reason} ->
                    ?SLOG(error, #{msg => "failed_to_start_rabbitmq_supervisor", reason => Reason}),
                    {error, Reason}
            end;
        {error, Reason} ->
            ?SLOG(error, #{msg => "failed_to_start_plugin_due_to_amqp_client_failure", reason => Reason}),
            {error, Reason}
    end.

%% @doc 停止应用程序。
stop(_State) ->
    ?SLOG(info, #{msg => "stopping_emqx_plugin_rabbitmq"}),
    emqx_plugin_rabbitmq_cli:unregister_cli(), %% 假设有 CLI 模块
    try
        Timeout = 10000, %% 10 seconds timeout
        {ok, TimerRef} = timer:exit_after(Timeout, timeout),
        try
            Result = emqx_plugin_rabbitmq:unload(),
            timer:cancel(TimerRef),
            ?SLOG(info, #{msg => "unload_emqx_plugin_rabbitmq_completed", result => Result}),
            ok
        catch
            Class1:Error1:Stack1 ->
                timer:cancel(TimerRef),
                ?SLOG(error, #{msg => "unload_emqx_plugin_rabbitmq_error",
                                class => Class1, error => Error1, stack => Stack1}),
                ok
        end
    catch
        exit:timeout ->
            ?SLOG(warning, #{msg => "unload_emqx_plugin_rabbitmq_timeout_forcing_cleanup"}),
            %% Add any RabbitMQ specific cleanup if necessary, e.g., persistent_term keys
            emqx_hooks:del('message.publish', {emqx_plugin_rabbitmq, on_message_publish}),
            catch ets:delete(?PLUGIN_RABBITMQ_TAB), %% Assuming a similar ETS table
            ok;
        Class2:Error2:Stack2 ->
            ?SLOG(error, #{msg => "unexpected_error_during_plugin_shutdown",
                            class => Class2, error => Error2, stack => Stack2}),
            ok
    end.

%%%===================================================================
%%% Internal functions
%%%===================================================================

ensure_amqp_client_started() ->
    case application:ensure_all_started(amqp_client) of
        {ok, StartedApps} ->
            ?SLOG(info, #{msg => "amqp_client_application_started", started_apps => StartedApps}),
            ok;
        {error, Reason} ->
            ?SLOG(error, #{msg => "failed_to_start_amqp_client_application", reason => Reason}),
            {error, {amqp_client_start_failed, Reason}}
    end.

handle_supervisor_started(Sup) ->
    case emqx_plugin_rabbitmq:load() of
        {ok, _} ->
            register_cli_and_log(),
            {ok, Sup};
        ok ->
            register_cli_and_log(),
            {ok, Sup};
        {error, Reason} ->
            ?SLOG(error, #{msg => "failed_to_load_rabbitmq_plugin", reason => Reason}),
            {error, Reason};
        Other ->
            ?SLOG(error, #{msg => "unexpected_error_loading_rabbitmq_plugin", error => Other}),
            {error, {unexpected_error, Other}}
    end.

register_cli_and_log() ->
    emqx_ctl:register_command(emqx_plugin_rabbitmq, {emqx_plugin_rabbitmq_cli, cmd}),
    ?SLOG(info, #{msg => "rabbitmq_plugin_started_successfully"}).

copy_config_to_etc() ->
    try
        PrivConfigPath = case code:priv_dir(emqx_plugin_rabbitmq) of
            {error, _} ->
                ?SLOG(warning, #{msg => "cannot_find_priv_dir_for_rabbitmq_plugin"}),
                undefined;
            PrivDir ->
                filename:join(PrivDir, "emqx_plugin_rabbitmq.hocon")
        end,

        case PrivConfigPath =/= undefined andalso filelib:is_regular(PrivConfigPath) of
            true ->
                case get_emqx_etc_dir() of
                    undefined ->
                        ?SLOG(warning, #{msg => "cannot_find_emqx_etc_dir_for_rabbitmq_config"});
                    EtcDir ->
                        TargetConfigPath = filename:join(EtcDir, "emqx_plugin_rabbitmq.hocon"),
                        NeedUpdate = case filelib:is_regular(TargetConfigPath) of
                            true ->
                                {ok, PrivInfo} = file:read_file_info(PrivConfigPath),
                                {ok, TargetInfo} = file:read_file_info(TargetConfigPath),
                                PrivInfo#file_info.mtime > TargetInfo#file_info.mtime;
                            false ->
                                true
                        end,
                        case NeedUpdate of
                            true ->
                                case file:copy(PrivConfigPath, TargetConfigPath) of
                                    {ok, _} ->
                                        ?SLOG(info, #{msg => "rabbitmq_config_file_copied_to_etc",
                                                    source => PrivConfigPath,
                                                    target => TargetConfigPath});
                                    {error, CopyReason} ->
                                        ?SLOG(warning, #{msg => "failed_to_copy_rabbitmq_config_file",
                                                       reason => CopyReason,
                                                       source => PrivConfigPath,
                                                       target => TargetConfigPath})
                                end;
                            false ->
                                ?SLOG(info, #{msg => "rabbitmq_config_file_already_up_to_date",
                                            path => TargetConfigPath})
                        end
                end;
            false ->
                ?SLOG(info, #{msg => "no_rabbitmq_config_file_in_priv_dir"})
        end
    catch
        Type:Error:Stack ->
            ?SLOG(warning, #{msg => "error_copying_rabbitmq_config_file",
                           type => Type,
                           error => Error,
                           stacktrace => Stack})
    end.

get_emqx_etc_dir() ->
    case os:getenv("EMQX_ETC_DIR") of
        false ->
            case application:get_env(emqx, etc_dir) of
                {ok, EtcDir} when is_list(EtcDir) ->
                    EtcDir;
                _ ->
                    case code:lib_dir(emqx) of
                        {error, _} ->
                            filename:join(filename:dirname(code:which(?MODULE)), "../etc");
                        EmqxPath ->
                            filename:join(filename:dirname(EmqxPath), "etc")
                    end
            end;
        EtcDir ->
            EtcDir
    end.
